package io.gitee.chearnee.fw.datasecurity.core.common;

import io.gitee.chearnee.fw.common.utils.AnnotationUtil;
import io.gitee.chearnee.fw.common.utils.FieldUtil;
import io.gitee.chearnee.fw.common.utils.ObjectUtil;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SDCacheUtil {
    private static final WeakHashMap<Class<?>, SDEntity> CLASS_SD_MAP = new WeakHashMap<>();

    /**
     * 得到对象
     *
     * @param obj
     * @return
     */

    @SneakyThrows
    public static SDEntity getBeanStringField(Object obj) {
        Class<?> targetBean = obj.getClass();
        if (targetBean.equals(Object.class)) {
            return null;
        }
        SDEntity fieldAnnotationMap = CLASS_SD_MAP.get(targetBean);
        if (fieldAnnotationMap != null) {
            return fieldAnnotationMap;
        }
        synchronized (CLASS_SD_MAP) {
            fieldAnnotationMap = CLASS_SD_MAP.get(targetBean);
            if (fieldAnnotationMap != null) {
                return fieldAnnotationMap;
            }
            if (!CLASS_SD_MAP.containsKey(targetBean)) {
                HashMap<Field, Annotation> fieldMap = new HashMap<>();
                ArrayList<Field> beanFields = new ArrayList<>();
                Field[] declaredFields = FieldUtil.getAllFields(targetBean);
                for (Field field : declaredFields) {
                    if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                        continue;
                    }
                    if (stringFieldProcessor(fieldMap, field)) continue;
                    if (iterableFieldProcessor(obj, targetBean, beanFields, field)) continue;
                    entityFieldProcessor(obj, targetBean, beanFields, field);
                }
                if (!CollectionUtils.isEmpty(fieldMap) || !CollectionUtils.isEmpty(beanFields)) {
                    fieldAnnotationMap = new SDEntity();
                    fieldAnnotationMap.beanFields = beanFields;
                    fieldAnnotationMap.stringFields = fieldMap;
                    CLASS_SD_MAP.put(targetBean, fieldAnnotationMap);
                    return fieldAnnotationMap;
                }
            }
            CLASS_SD_MAP.put(targetBean, null);
        }
        return null;
    }

    private static boolean stringFieldProcessor(HashMap<Field, Annotation> fieldMap, Field field) {
        if (String.class.isAssignableFrom(field.getType())) {
            AnnotationUtil.checkOneOfAnnotation(field, SDAnnotationCacheUtil.getCacheSDAnnotations())
                    .ifPresent(annotation -> fieldMap.put(field, AnnotationUtils.getAnnotation(annotation, annotation.annotationType())));
            return true;
        }
        return false;
    }

    private static boolean iterableFieldProcessor(Object obj, Class<?> targetBean, ArrayList<Field> beanFields, Field field) throws IllegalAccessException {
        if (Iterable.class.isAssignableFrom(field.getType())) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            Object value = field.get(obj);
            if (value != null) {
                for (Object next : (Iterable<?>) value) {
                    if (next != null) {
                        Class<?> fieldCls = next.getClass();
                        //如果是bean，并且不是递归
                        if (!fieldCls.equals(targetBean) && ObjectUtil.isBean(next)) {
                            getBeanStringField(next);
                            break;
                        }
                    }
                }
            }
            //warning:如果原本field是泛型，但实际value值是BigDecimal，那么可能还是会往下走
            beanFields.add(field);
            return true;
        }
        return false;
    }

    private static void entityFieldProcessor(Object obj, Class<?> targetBean, ArrayList<Field> beanFields, Field field) throws IllegalAccessException {
        if (FieldUtil.fieldIsEntity(field)) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            Object value = field.get(obj);
            if (value != null) {
                Class<?> fieldCls = value.getClass();
                //如果是bean，并且不是递归
                if (!fieldCls.equals(targetBean) && ObjectUtil.isBean(value)) {
                    getBeanStringField(value);
                }
            }
            beanFields.add(field);
        }
    }


    @Getter
    @Setter
    public static class SDEntity {
        private ArrayList<Field> beanFields = new ArrayList<>();
        private Map<Field, Annotation> stringFields = new HashMap<>();
    }
}
